//
// $Id: EngineAgent.java 8 2007-04-18 13:47:40Z z_hossain $
//
// xChess is a multiplatform graphical chess user interface
// Copyright (C) 2007  MD. Z. Hossain <z_hossain@users.sf.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
// 02110-1301, USA.
//
// For more information visit http://xchess.sourceforge.net
//

package control;

import event.EngineEvent;
import event.IEngineListener;
import gui.ChessClock;
import gui.IChessContext;

import java.io.IOException;
import java.util.ArrayList;

import model.Move;
import net.sourceforge.xchess.EngineIO;
import net.sourceforge.xchess.EngineXB;


public abstract class AbsEngineAgent extends AbsAgent {
	
	private EngineIO engineIO;
	private ArrayList<IEngineListener> listenerList = new ArrayList<IEngineListener>(5);
	private Thread parseThread;
		
	private String name;
	private int protocolId;
	
	public AbsEngineAgent(IChessContext context) {
		super(context);
		this.engineIO = new EngineIO("gnuchess");
		initiate();
	}
	
	public static AbsEngineAgent createEngine(IChessContext context) {
		return new EngineXB(context);
	}
	
	@Override
	public void moveDeclared(Move move) {
		chessClock.start();
		userMove(move);
	}
	
	public void addEngineListener(IEngineListener l) {
		listenerList.add(l);
	}
	
	public void removeEngineListener(IEngineListener l) {
		listenerList.remove(l);
	}
	
	protected void fireMovePrinted(EngineEvent e) {
		if(listenerList == null) return;
		int count = listenerList.size();
		for(int i = 0; i < count; i++) {
			listenerList.get(i).movePrinted(e);
		}
	}
	
	protected void fireIllegalPrinted(EngineEvent e) {
		if(listenerList == null) return;
		int count = listenerList.size();
		for(int i = 0; i < count; i++) {
			listenerList.get(i).illegalPrinted(e);
		}
	}
	
	protected void fireDataPrinted(EngineEvent e) {
		if(listenerList == null) return;
		int count = listenerList.size();
		for(int i = 0; i < count; i++) {
			listenerList.get(i).dataPrinted(e);
		}
	}
	
	protected void fireDataEntered(EngineEvent e) {
		if(listenerList == null) return;
		int count = listenerList.size();
		for(int i = 0; i < count; i++) {
			listenerList.get(i).dataEntered(e);
		}
	}
	
	protected void updateContext(Move move) {
		boardUI.update(move.getAffectedSquares(move.doMove(boardUI.getBoard())));
		moveListUI.addMove(move);
		chessClock.stop();
		chessClock.switchTurn();
	}
	
	public int getProtocolId() {
		return protocolId;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public void initiate() {
		parseThread = new Thread() {
			@Override
			public void run() {
				while(true) {
					try { parseCommand(); } 
					catch (IOException e) { e.printStackTrace(); break; }
				}
			}
		};
		parseThread.start();
	}
	
	public ChessClock getChessClock() {
		return chessClock;
	}

	public void setChessClock(ChessClock chessClock) {
		this.chessClock = chessClock;
	}
	
	public String readLine() throws IOException {
		String line = engineIO.readLine();
		if(line != null && line.length() > 0) {
			fireDataPrinted(new EngineEvent(this, line));
			return line;
		} else return null;
	}

	public void writeLine(String string) {
		fireDataEntered(new EngineEvent(this, string));
		engineIO.writeLine(string);
	}
	
	abstract protected void parseCommand() throws IOException;
	abstract public void userMove(Move move);
	abstract public void setMoveTime(int move, long time);
	abstract public void setBlitzTime(long base, long increment);
	abstract public void setPondering(boolean on);
	abstract public void quitEngine();

	@Override
	public void dispose() {
		super.dispose();
		parseThread = null;
		quitEngine();
		engineIO.destroy();
	}
}
